
import { Node, Sprite, Tween, tween, __private } from "cc";
import { GLoader } from "fairygui-cc";
import { BehaviorBase } from "../../frameworks/base/BehaviorBase";
import { EventSystem } from "../../frameworks/system/EventSystem";
import { IComponent } from "../common/IComponent";
import { Res } from "../../frameworks/res/Res";
import ResLoadQueue from "../../frameworks/res/ResLoadQueue";
import { TimerManager } from "../../frameworks/base/TimerManager";


export enum LifeState {
    Empty,
    Loading,
    LoadSucceed,
    LoadFailure,
}


export class MediatorBase extends BehaviorBase {

    protected _object: any;
    protected parameter: any;
    private _loadQueue: ResLoadQueue = new ResLoadQueue();
    private _listeners!: Array<any>;
    private _attachs!: Array<any>;
    private _components!: Array<IComponent | Node> | null;

    /**
     * 精灵对象
     */
    protected spriteTargets!: Array<any> | null;

    /**
     * fgui加载器对象
     */
    protected loaderTargets!: Array<GLoader> | null;

    /**
     * tween动画对象列表
     */
    protected tweenTargets!: Array<any> | null;

    /**
     * 时钟id列表
     */
    protected timeoutIds!: Array<number> | null;
    protected intervalIds!: Array<number> | null;

    /**
     * 0-空闲状态 1-加载中 2-加载完成 3-销毁了
     */
    protected lifeState: LifeState = LifeState.Empty;

    /**
     * 显示对象
     * @param enable 
     */
    public set visible(enable: boolean) {

    }

    /**
     * 显示对象
     * @param enable 
     */
    public get visible(): boolean {
        return false;
    }

    /**
     * 表现实例
     */
    public get view(): any {
        return this._object;
    }

    /**
     * 初始化
     */
    public async start(parameter?: any) {
        if (this.lifeState != LifeState.Empty) {
            return;
        }
        this.lifeState = LifeState.Loading;
        this.parameter = parameter;
        this.onPrepare();
        await this.preload();
    }

    /**
     * 设置参数
     * @param parameter 
     */
    public setParameter(parameter: any) {
        this.parameter = parameter;
    }

    /**
     * 加载准备
     * @param queue 
     */
    protected loadPrepare(queue: ResLoadQueue) {
        this.onLoadPrepare(queue);
    }

    /**
     * 加载准备
     * @param queue 
     */
    protected onLoadPrepare(queue: ResLoadQueue) {

    }

    /**
     * 开始加载
     */
    private async preload() {

        this._loadQueue.clear();
        this.loadPrepare(this._loadQueue);
        await this._loadQueue.load();

        this._object = await this.loadMain();

        // 主体对象加载失败
        if (this._object == null) {
            this.lifeState == LifeState.LoadFailure;
            this.clear();
            return;
        }

        await this.loadComponent();

        // 实例已经被删除
        if (this.lifeState == LifeState.Empty) {
            this.clear();
            return;
        }

        // 加载成功
        this.attach(this._object);
    }



    /**
     * 绑定实例对象
     * @param obj 
     */
    public attach(obj: any) {

        if (obj == null) {
            console.error("绑定的实例对象为空");
            return;
        }

        if (this.lifeState == LifeState.LoadSucceed) {
            console.error("绑定的实例对象已经被绑定");
            return;
        }

        this.lifeState = LifeState.LoadSucceed;
        this.visible = true;
        obj["_mediator"] = this;

        this.inject();
        this.onAttach();
        this.onAwake();
        this.onStart();
        this.onEnable();
        this.onRefresh();
    }

    /**
     * 准备
     */
    protected onPrepare() {

    }

    /**
     * 关联表现对象
     */
    protected onAttach() {

    }

    /**
     * 加载主资源
     */
    protected async loadMain() {

    }

    /**
     * 加载其他资源
     */
    protected async loadComponent() {
        if (this._attachs) {
            let that: any = this;
            if (!this._components) this._components = [];
            for (let key in this._attachs) {
                let _attach = this._attachs[key];
                let path = _attach[1];
                let classType = _attach[2];
                let args = _attach[3];
                let target = this.getChild(path) || this.view;
                if (target) {
                    if (typeof classType === 'string') {
                        let obj = await Res.spawnAsync(classType);
                        if (obj != null) {
                            that[key] = obj;
                            target.node.addChild(obj);
                            this._components.push(obj);
                        }
                    }
                    else {
                        let resPath = classType["resPath"];
                        let objName = classType["objName"];
                        if (resPath && objName) {
                            let comp = new classType();
                            comp["resPath"] = resPath;
                            comp["objName"] = objName;
                            await comp.init(target, args, true);
                            that[key] = comp;
                            this._components.push(comp);
                        }
                        else {
                            let comp = new classType();
                            await comp.init(target, args, false);
                            that[key] = comp;
                            this._components.push(comp);
                        }
                    }
                }
            }
        }
    }

    /**
     * 通过路径获取子节点
     * @param path 
     * @returns 
     */
    public getChild(path: string): any {
        return;
    }

    /**
     * 注入数据
     */
    protected inject() {

        let that: any = this;

        if (that["update"]) {
            TimerManager.inst.addUpdate(this);
        }

        if (that["lateUpdate"]) {
            TimerManager.inst.addLateUpdate(this);
        }

        // 监听事件
        if (this._listeners) {
            let that: any = this;
            for (let key in this._listeners) {
                let name = this._listeners[key];
                let func: Function = that[name];
                EventSystem.on(key, func, this);
            }
        }
    }

    /**
     * 显示控件
     * 如果控件没有加载是没效果的
     * 如果控件在隐藏状态，会触发onEnable
     */
    public show(parameter?: any) {
        this.parameter = parameter;
        if (!this.visible) {
            this.visible = true;
            this.onEnable();
            this.onRefresh();
        }
        else {
            this.onRefresh();
        }
    }

    /**
     * 刷新
     */
    protected onRefresh() {

    }

    /**
     * 隐藏控件
     * 如果控件没有加载是没效果的
     * 如果控件在激活状态，会触发onDisable
     */
    public hide() {
        if (this.visible) {
            this.visible = false;
            this.onDisable();
        }
    }

    /**
     * 监听事件，销毁的时候自动注销
     * @param eventName 
     * @param handler 
     */
    protected on(eventName: string, handler: Function) {
        EventSystem.on(eventName, handler, this);
    }

    /**
     * 取消监听事件
     * @param eventName 
     * @param handler 
     */
    protected off(eventName: string, handler: Function) {
        EventSystem.off(eventName, handler, this);
    }

    /**
     * 销毁
     */
    public destroy() {
        if (this.lifeState == LifeState.Empty)
            return;
        this.lifeState = LifeState.Empty;
        EventSystem.offAll(this);
        TimerManager.inst.remove(this);
        if (this._object != null) {
            this.onDisable();
            this.onDestroy();
            this.clear();
        }
    }

    /**
     * 回收
     */
    protected clear() {

        this._loadQueue.clear();

        if (this._components) {
            for (let i = 0; i < this._components.length; ++i) {
                let comp = this._components[i];
                if (comp instanceof Node) {
                    Res.recycle(comp);
                }
                else {
                    comp.destroy();
                }
            }
            this._components = null;
        }

        if (this.spriteTargets) {
            for (let i = 0; i < this.spriteTargets.length; ++i) {
                Res.clearSprite(this.spriteTargets[i]);
            }
            this.spriteTargets = null;
        }

        if (this.loaderTargets) {
            for (let i = 0; i < this.loaderTargets.length; ++i) {
                let image = this.loaderTargets[i].texture;
                if (image) {
                    image.destroy();
                }
            }
            this.loaderTargets = null;
        }

        if (this.tweenTargets) {
            for (let i = 0; i < this.tweenTargets.length; ++i) {
                Tween.stopAllByTarget(this.tweenTargets[i]);
            }
            this.tweenTargets = null;
        }

        if (this.timeoutIds) {
            for (let i = 0; i < this.timeoutIds.length; ++i) {
                clearTimeout(this.timeoutIds[i]);
            }
            this.timeoutIds = null;
        }

        if (this.intervalIds) {
            for (let i = 0; i < this.intervalIds.length; ++i) {
                clearInterval(this.intervalIds[i]);
            }
            this.intervalIds = null;
        }

    }


    /**
     * 是否已经激活
     * @returns 
     */
    public get isActive(): boolean {
        return this._object != null;
    }



    /**
     * 所有通过这个的函数，在界面销毁的时候都会移除动画
     * @param target 
     * @returns 
     */
    public tween<T>(target?: T): Tween<T> {
        if (!this.tweenTargets) {
            this.tweenTargets = [target];
        }
        else {
            let has = false;
            for (let i = 0; i < this.tweenTargets.length; ++i) {
                if (this.tweenTargets[i] == target) {
                    has = true;
                }
            }
            if (!has)
                this.tweenTargets.push(target);
        }
        return tween(target);
    }

    /**
     * 延迟执行
     * @param callbcak 
     * @param timeout 
     * @param args 
     */
    public setTimeout(callbcak: Function, timeout: number, ...args: any[]): number {
        let that = this;
        let id = setTimeout((args: any[]) => {
            let timerIds = that.timeoutIds;
            if (timerIds) {
                let index = timerIds.indexOf(id);
                if (index != -1)
                    timerIds.splice(index, 1);
            }
            callbcak.call(that, ...args);
        }, timeout, args);
        if (!this.timeoutIds) this.timeoutIds = [];
        this.timeoutIds.push(id);
        return id;
    }

    /**
     * 清理间隔执行
     * @param id 
     */
    public clearTimeout(id: number) {
        clearTimeout(id);
        let timeoutIds = this.timeoutIds;
        if (timeoutIds) {
            let index = timeoutIds.indexOf(id);
            if (index != -1)
                timeoutIds.splice(index, 1);
        }
    }

    /**
     * 间隔执行
     * @param callbcak 
     * @param timeout 
     * @param args 
     */
    public setInterval(callbcak: Function, timeout: number, ...args: any[]): number {
        let id = setInterval(callbcak, timeout, args);
        if (!this.intervalIds) this.intervalIds = [];
        this.intervalIds.push(id);
        return id;
    }

    /**
     * 清理间隔执行
     * @param id 
     */
    public clearInterval(id: number) {
        clearInterval(id);
        let intervalIds = this.intervalIds;
        if (intervalIds) {
            let index = intervalIds.indexOf(id);
            if (index != -1)
                intervalIds.splice(index, 1);
        }
    }

    /**
     * 获取预加载的对象，对象不用删除，会跟着界面一起销毁的
     * @param name 
     * @returns 
     */
    public getObject<T>(name: string): T {
        return this._loadQueue.getObject(name)
    }


    /**
     * 设置精灵对象的图片
     * @param target 
     * @param path 
     */
    public setSprite(target: GLoader | Sprite, path: string | null) {
        if (!this.spriteTargets) this.spriteTargets = [];
        this.spriteTargets.push(target);
        Res.setSprite(target, path);
    }

    /**
     * 设置FGUI加载器的地址
     * @param target 
     * @param url 
     */
    public setLoaderUrl(target: GLoader, url: string) {
        if (!this.loaderTargets) this.loaderTargets = [];
        this.loaderTargets.push(target);
        target.url = url;
    }

    /**
     * 设置精灵对象的图片通过图集
     * @param target 
     * @param name 精灵的名字
     * @param path 图集路径 
     */
    public setSpriteByAtlas(target: any | null, name: string, path: string) {
        if (!this.spriteTargets) this.spriteTargets = [];
        this.spriteTargets.push(target);
        Res.setSpriteByAtlas(target, name, path);
    }
}